<template>
  <div :class="rootClass">
    <JsonTreeCell
      :data="data" @click.native="onCellClicked"
      :editable="selected && editable"
      :selected="selected"
      @createChildren="onCreateChildren"
      ref="treeCell"
    ></JsonTreeCell>
    <transition name="collapse"
      @before-enter="onBeforeUncollapse"
      @before-leave="onBeforeCollapse"
    >
        <div class="children" v-show="!collapsed" v-if="childrenDataList.length > 0" ref="children">
          <template  v-for="(childrenData, index) of childrenDataList">
            <JsonTree :data="childrenData"
              :root="rootJsonTree"
              :key="index"
              :editable="editable"
              @selected="(stack) => bubble('selected', stack)"
              @createChildren="(stack) => bubble('createChildren', stack)"
              @unselected="(stack) => bubble('unselected', stack)"
              @resize="onChildrenCollapaseStateChanged"
              ref="childrenTree"
            />
          </template>
        </div>
    </transition>
    
    <label v-if="childrenDataList.length > 0" class="collapse-toggle-trigger">
      <v-icon class="icon" name="plus-square" v-if="this.collapsed"></v-icon>
      <v-icon class="icon" name="minus-square" v-else></v-icon>
      <input type="checkbox" v-model="collapsed" style="display: none;"/>
    </label>
    <div class="leftSideBorder" ref="leftSideBorder"></div>
  </div>
</template>

<script>
import { default as JsonTreeCell, ICON_TYPE as IconTypeEnum, DATA_TYPE as DataObject } from './JsonTreeCell'

const JsonTree = {
  name: 'JsonTree',
  components: { JsonTree, JsonTreeCell },
  props: {
    data: {
      type: Object,
      required: true,
    },
    root: {
      type: JsonTree,
      default: null,
    },
    editable: {
      type: Boolean,
      default: true,
    }
  },
  computed: {
    rootClass() {
      return [
        'jsonTreeItem',
        this.collapsed ? 'collapsed' : 'uncollapsed',
        this.root == null ? 'jsonTreeRoot' : 'jsonTreeChildren',
        this.selected ? 'selected' : '',
      ];
    },
    childrenDataList() {
      return this.data.children || [];
    },
    rootJsonTree() {
      return this.root || this;
    },
    ICON_TYPE: {
      get: function() {
        return IconTypeEnum;
      },
    },
  },
  data() {
    return {
      collapsed: true,
      selected: false,
      selectedJsonTree: null,
      editing: false,
    };
  },
  methods: {
    onCellClicked() {
      const vm = this;
      vm.select();
    },
    select() {
      const vm = this;
      let originalSelectedJsonTreeItem = null;
      if (null == vm.root) {
        originalSelectedJsonTreeItem = vm.selectedJsonTree;
        vm.selectedJsonTree = vm;
      } else {
        originalSelectedJsonTreeItem = vm.root.selectedJsonTree;
        vm.root.selectedJsonTree = vm;
      }
      if (null != originalSelectedJsonTreeItem && originalSelectedJsonTreeItem != this) {
        originalSelectedJsonTreeItem.selected = false;
      }
      vm.selected = true;
    },
    /* animation */
    onBeforeUncollapse() {
      const vm = this;
      vm.refreshChildrenElSize(0, 0);
      vm.triggerRefreshSize();
    },
    onBeforeCollapse() {
      this.refreshChildrenElSize(0, 0);
      this.editing = false;
    },
    bubble(evt, arr) {
      if (arr == null) {
        return this.$emit(evt, [this]);
      } else {
        return this.$emit(evt, arr.concat(this));
      }
    },
    refreshChildrenElSize(specifiedTotalHeight=null, specifiedBarHeight=null) {
      let $childrenEle = this.$refs.children;
      let $leftSideBorderEle = this.$refs.leftSideBorder;
      let totalHeight = 0;
      if (null !== specifiedTotalHeight) {
        totalHeight = specifiedTotalHeight;
      } else {
        if (null != this.$refs.childrenTree) {
          for (let childrenTree of this.$refs.childrenTree) {
            totalHeight += childrenTree.$refs.treeCell.$el.clientHeight + childrenTree.childrenHeight;
          }
        }
      }
      if ($childrenEle.clientHeight != totalHeight) {
        // no margin/padding
        window.requestAnimationFrame(() => this.$emit('resize', totalHeight - this.childrenHeight));
      }
      let barHeight = totalHeight;
      let lastChildrenTree = this.$refs.childrenTree && this.$refs.childrenTree[this.$refs.childrenTree.length-1];
      if (null != lastChildrenTree && !lastChildrenTree.collapsed) {
        barHeight -= lastChildrenTree.childrenHeight;
      }
      if (null != specifiedBarHeight) {
        barHeight = specifiedBarHeight;
      }
      $childrenEle.style.height = totalHeight + "px";
      $leftSideBorderEle.style.height = barHeight + "px";
      this.childrenHeight = totalHeight;
    },
    triggerRefreshSize() {
      const vm = this;
      if (null != vm._nextResizeAnimationFrame) {
        window.cancelAnimationFrame(vm._nextResizeAnimationFrame);
        vm._nextResizeAnimationFrame = null;
      }
      vm._nextResizeAnimationFrame = window.requestAnimationFrame(function() {
        vm.refreshChildrenElSize();
        vm._nextResizeAnimationFrame = null;
      });
    },
    onChildrenCollapaseStateChanged() {
      this.triggerRefreshSize();
    },
    onCreateChildren() {
      this.collapsed = false;
      this.bubble('createChildren', []);
    }
  },
  watch: {
    collapsed(newValue, oldValue) {
      const vm = this;
      if (false == newValue && true == oldValue) {
        vm.$emit('uncollapse');
      } else if (true == newValue && false == oldValue) {
        vm.$emit('collapse');
      }
    },
    editing(newValue) {
      if (newValue === true) {
        if (this.collapsed && this.childrenDataList.length > 0) {
          this.collapsed = false;
        }
      } else {
        Object.assign(this.editingData, DataObject);
      }
    },
    selected(newValue, oldValue) {
      const vm = this;
      if (newValue === false) {
        vm.editing = false;
      }
      vm.$nextTick(function() {
        if (false == newValue && oldValue == true) {
          vm.$emit('unselected', [vm]);
        }
        vm.$nextTick(function() {
          if (true == newValue && oldValue == false) {
            vm.$emit('selected', [vm]);
          }
        })
      })
      
    }
  },
  created() {
    const vm = this;
    if (null == vm.root) {
      vm.select();
    } else {
      vm.selected = vm.rootJsonTree.selectedJsonTree == vm;
    }
    vm.childrenHeight = 0;
    vm._nextResizeAnimationFrame = null;
  },
};

export default JsonTree;

</script>

<style scoped>
/* root element style */
.jsonTreeItem {
  display: inline-block;
  box-sizing: border-box;
  position: relative;
  font-size: 1rem;
}

.jsonTreeChildren::after {
  content: "";
  display: block;
  position: absolute;
  top: .75rem;
  left: -2rem;
  right: 100%;
  border-bottom: 1px dashed #777;
}

.jsonTreeRoot {
  width: 20rem;
}
.jsonTreeChildren {
  width: 100%;
}

/* leftSideBorder */
.leftSideBorder {
  position: absolute;
  top: .75rem;
  left: 1rem;
  border-left: 1px dashed #777;
  z-index: -1;
}

/* children list style */
.children {
  margin: 0;
  padding: 0 0 0 3rem;
  overflow: hidden;
  box-sizing: border-box;
  /* font-size: 0;防止inline-block间的换行符所产生的间隙 */
  font-size: 0;
}

/* transition */
.leftSideBorder,
.children,
.children.collapse-enter-active,
.children.collapse-leave-active {
  transition: height .3s;
}

/* trigger style */
.collapse-toggle-trigger {
  width: 1.5rem;
  height: 1.5rem;
  line-height: 1.5rem;
  text-align: center;
  background: white;
  position: absolute;
  left: .25rem;
  top: 0;
}

/* icon style */
.icon {
  vertical-align: middle;
}
.font-icon {
  border-radius: 2px;
  background: #ddd;
  display: block;
  padding: .1rem;
  line-height: 1rem;
  color: white;
}
</style>
